package main

import (
	"context"
	_ "embed"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"os/exec"
	"os/signal"
	"syscall"
	"time"

	"github.com/creack/pty"
	"github.com/gorilla/websocket"
	"github.com/urfave/cli/v2"
)

var (
	version = "1.0.0"
	commit  = "dev"
	date    = time.Now().Format("2006-01-02")
)

//go:embed assets/terminal.html
var htmlContent string

var upgrader = websocket.Upgrader{
	CheckOrigin: func(r *http.Request) bool {
		return true // 仅测试用！生产环境请严格校验 Origin
	},
}

func getShellCommand() *exec.Cmd {
	// 尝试 zsh -> bash -> sh
	for _, shell := range []string{"zsh", "bash", "sh"} {
		if path, err := exec.LookPath(shell); err == nil {
			cmd := exec.Command(path)
			cmd.Env = os.Environ()
			return cmd
		}
	}
	// fallback
	return exec.Command("sh")
}

func terminalHandler(w http.ResponseWriter, r *http.Request) {
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println("WebSocket 升级失败:", err)
		return
	}
	defer conn.Close()

	cmd := getShellCommand()

	ptmx, err := pty.Start(cmd)
	if err != nil {
		log.Println("启动 PTY 失败:", err)
		return
	}
	defer ptmx.Close()

	// WebSocket → PTY
	go func() {
		defer ptmx.Close()
		for {
			_, data, err := conn.ReadMessage()
			if err != nil {
				return
			}
			ptmx.Write(data)
		}
	}()

	// PTY → WebSocket
	go func() {
		defer conn.Close()
		buf := make([]byte, 1024)
		for {
			n, err := ptmx.Read(buf)
			if err != nil {
				if err != io.EOF {
					log.Println("PTY 读取错误:", err)
				}
				return
			}
			if n > 0 {
				if err := conn.WriteMessage(websocket.TextMessage, buf[:n]); err != nil {
					return
				}
			}
		}
	}()

	cmd.Wait()
}

func healthHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, `{"status":"ok","version":"%s","uptime":"%s"}`, version, time.Now().Format(time.RFC3339))
}
func main() {
	app := &cli.App{
		Name:    "web-terminal",
		Version: fmt.Sprintf("%s (build: %s, date: %s)", version, commit, date),
		Usage:   "一个专业的 Web 终端服务",
		Authors: []*cli.Author{
			{
				Name:  "Your Name",
				Email: "your.email@example.com",
			},
		},
		HideVersion: true,
		Flags: []cli.Flag{
			&cli.IntFlag{
				Name:    "port",
				Aliases: []string{"p"},
				Value:   15341,
				Usage:   "服务监听端口",
				EnvVars: []string{"WEB_TERMINAL_PORT", "PORT"},
			},
			&cli.StringFlag{
				Name:    "host",
				Aliases: []string{"H"},
				Value:   "0.0.0.0", // ✅ 默认改为 0.0.0.0
				Usage:   "服务监听地址（0.0.0.0 表示所有网络接口）",
				EnvVars: []string{"WEB_TERMINAL_HOST", "HOST"},
			},
			&cli.BoolFlag{
				Name:    "verbose",
				Aliases: []string{"V"},
				Value:   false,
				Usage:   "详细日志输出",
				EnvVars: []string{"WEB_TERMINAL_VERBOSE"},
			},
		},
		Action: runServer,
		Commands: []*cli.Command{
			{
				Name:    "version",
				Aliases: []string{"v"},
				Usage:   "显示版本信息",
				Action: func(c *cli.Context) error {
					fmt.Printf("web-terminal version %s\n", version)
					fmt.Printf("Commit: %s\n", commit)
					fmt.Printf("Build date: %s\n", date)
					return nil
				},
			},
		},
		Before: func(c *cli.Context) error {
			if c.Bool("verbose") {
				log.SetFlags(log.LstdFlags | log.Lshortfile)
			} else {
				log.SetFlags(log.LstdFlags)
			}
			return nil
		},
	}

	if err := app.Run(os.Args); err != nil {
		log.Fatal(err)
	}
}

func runServer(c *cli.Context) error {
	port := c.Int("port")
	host := c.String("host")
	verbose := c.Bool("verbose")

	if verbose {
		log.Printf("启动参数: host=%s, port=%d, verbose=%t", host, port, verbose)
	}

	http.HandleFunc("/ws", terminalHandler)
	http.HandleFunc("/health", healthHandler) // 建议保留健康检查
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/html; charset=utf-8")
		w.Write([]byte(htmlContent))
	})

	addr := fmt.Sprintf("%s:%d", host, port)

	log.Printf("🚀 Web 终端服务启动中...")
	log.Printf("📍 监听地址: %s", addr)

	// 智能提示访问地址
	if host == "0.0.0.0" || host == "" {
		log.Printf("🌐 请通过以下地址访问:")
		log.Printf("   → 本地: http://localhost:%d", port)
		log.Printf("   → 局域网或远程: http://<你的IP>:%d", port)
		log.Printf("📊 健康检查: http://localhost:%d/health", port)
	} else {
		url := fmt.Sprintf("http://%s:%d", host, port)
		log.Printf("🌐 访问地址: %s", url)
		log.Printf("📊 健康检查: %s/health", url)
	}

	server := &http.Server{
		Addr:    addr,
		Handler: nil,
	}

	go func() {
		if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			log.Fatalf("服务器启动失败: %v", err)
		}
	}()

	quit := make(chan os.Signal, 1)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
	<-quit

	log.Printf("🛑 正在关闭服务器...")

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	if err := server.Shutdown(ctx); err != nil {
		log.Fatalf("服务器关闭失败: %v", err)
	}

	log.Printf("✅ 服务器已正常关闭")
	return nil
}